package com.maimao.utils.excel;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.maimao.utils.excel.convert.LocalDateConverter;
import com.maimao.utils.excel.convert.LocalDateTimeConverter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * EasyExcel通用导出工具
 *
 * @author MaoLin Wang
 * @date 2021/4/27 下午12:37
 */
public class ExcelUtil<T> {

    public static final String EXCEL_SUFFIX = ".xlsx";
    private Class<?> clazz;
    private List<T> data;
    private String fileName;
    private HttpServletResponse response;
    private Converter[] converts;
    private int[] mergeColumns;

    /**
     * Excel单sheet最大导出量
     */
    public static final Integer EXCEL_MAX_EXPORT_SIZE = 10;

    public ExcelUtil() {
    }

    /**
     * @param clazz    实体class对象
     * @param data     数据集
     * @param fileName 文件名
     * @param response response
     */
    public ExcelUtil setMustParams(Class<?> clazz, List<T> data, String fileName, HttpServletResponse response) {
        this.clazz = clazz;
        this.data = data;
        this.fileName = fileName + LocalDateTime.now().getNano();
        this.response = response;
        return this;
    }

    public static <T> ExcelUtil builder() {
        return new ExcelUtil<T>();
    }


    /**
     * 普通导出
     *
     * @throws IOException
     */
    public void export() throws IOException {
        setResponse(fileName, response);
        ExcelWriterBuilder excelWriterBuilder = EasyExcel
                .write(response.getOutputStream())
                .head(clazz);
        doPrint(data, excelWriterBuilder, false);
    }

    /**
     * 有合并行需求的导出
     *
     * @param mergeUpToMap nullable
     * @throws IOException
     */
    public void exportWithMerge(Map<Integer, Set<Integer>> mergeUpToMap) throws IOException {

        setResponse(fileName, response);
        ExcelWriterBuilder excelWriterBuilder = EasyExcel
                .write(response.getOutputStream())
                .head(clazz)
//                .registerWriteHandler(new MergeStrategy(data.size(), mergeColumns));
                .registerWriteHandler(new ExcelFillCellMergeStrategy(1, mergeColumns,mergeUpToMap));

        doPrint(data, excelWriterBuilder, false);
    }

    /**
     * 导出到本地
     *
     * @param filePath 本地导出路径
     * @throws IOException
     */
    public void exportToLocal(String filePath) throws IOException {
        String datePath = StringUtils.substringBeforeLast(filePath, "/");
        if (!FileUtil.exist(datePath)){
            File file=new File(datePath);
            file.mkdir();
        }
        ExcelWriterBuilder excelWriterBuilder = EasyExcel
                .write(new FileOutputStream(filePath + fileName + EXCEL_SUFFIX))
                .head(clazz);
        if (ObjectUtil.isNotEmpty(this.mergeColumns)) {
            excelWriterBuilder.registerWriteHandler(new MergeStrategy(data.size(), this.mergeColumns));
        }
        doPrint(data, excelWriterBuilder, false);
    }

    /**
     * 上传后转成inputStream用于云上传
     *
     * @throws IOException
     */
    public ByteArrayInputStream exportToInputStream() throws IOException {

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ExcelWriterBuilder excelWriterBuilder = EasyExcel
                .write(outputStream)
                .head(clazz);
        if (ObjectUtil.isNotEmpty(this.mergeColumns)) {
            excelWriterBuilder.registerWriteHandler(new MergeStrategy(data.size(), this.mergeColumns));
        }
        doPrint(data, excelWriterBuilder, false);
        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        return inputStream;
    }

    private void doPrint(List<T> list, ExcelWriterBuilder excelWriterBuilder, boolean template) {
        excelWriterBuilder.registerConverter(new LocalDateConverter());
        excelWriterBuilder.registerConverter(new LocalDateTimeConverter());
        if (this.converts != null && this.converts.length > 0) {
            for (Converter<Object> convert : this.converts) {
                excelWriterBuilder.registerConverter(convert);
            }
        }
        ExcelWriter excelWriter = excelWriterBuilder.build();
        try {
            if (list.size() > EXCEL_MAX_EXPORT_SIZE) {
                int sheetCnt = list.size() % EXCEL_MAX_EXPORT_SIZE > 0 ? list.size() / EXCEL_MAX_EXPORT_SIZE + 1 : list.size() / EXCEL_MAX_EXPORT_SIZE;

                for (int i = 1; i <= sheetCnt; i++) {
                    int start = (i - 1) * EXCEL_MAX_EXPORT_SIZE;
                    List<T> sub = ListUtil.sub(list, start, start + EXCEL_MAX_EXPORT_SIZE);
                    WriteSheet writeSheet = EasyExcel.writerSheet(i).build();
                    if (template) {
                        excelWriter.fill(sub, writeSheet);
                    } else {
                        excelWriter.write(sub, writeSheet);
                    }
                }
            } else {
                WriteSheet writeSheet = EasyExcel.writerSheet().build();
                if (template) {
                    excelWriter.fill(list, writeSheet);
                } else {
                    excelWriter.write(list, writeSheet);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    /**
     * 使用模板导出
     *
     * @param templateName 模板名
     */
    public void exportWithTemplate(Class<T> clazz, List<T> list, String templateName, HttpServletResponse response, HttpSession session) throws IOException {

        setResponse(templateName, response);
        String path = session.getServletContext().getRealPath("/") + "excel/templates/" + templateName + ".xlsx";
        ExcelWriterBuilder excelWriterBuilder = EasyExcel
                .write(response.getOutputStream())
                .withTemplate(path)
                .head(clazz);
        doPrint(list, excelWriterBuilder, true);
    }

    private static void setResponse(String fileName, HttpServletResponse response) {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("UTF-8");
        try {
            fileName = URLEncoder.encode(fileName, "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }


    public static <T> List<T> importExcellist(MultipartFile file, Class<T> clazz) throws IOException {
        //通过EasyExcel自动解析并获取数据
        List<T> list = EasyExcel.read(file.getInputStream())
                .head(clazz)
                .sheet(0)
                .doReadSync();  //将解析结果封装为list集合返回
        return list;
    }

    public ExcelUtil<T> setConverts(Converter... converts) {
        this.converts = converts;
        return this;
    }

    public Converter<Object>[] getConverts() {
        return converts;
    }

    public int[] getMergeColumns() {
        return mergeColumns;
    }

    public ExcelUtil<T> setMergeColumns(int... mergeColumns) {
        this.mergeColumns = mergeColumns;
        return this;
    }


    public Class<?> getClazz() {
        return clazz;
    }


    public List<T> getData() {
        return data;
    }


    public String getFileName() {
        return fileName;
    }


    public HttpServletResponse getResponse() {
        return response;
    }

}
